package com.gitee.ly;

import com.gitee.ly.model.ServiceInfo;
import com.gitee.ly.reentranlock.ServiceNameReenTranLock;
import lombok.extern.slf4j.Slf4j;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
public class ServiceManage {
    private static final Map<String, List<ServiceInfo>> serviceInfoMap = new HashMap<>();
    private static final long reenTranLockTimeOutSeconds = 60;
    private static final ScheduledThreadPoolExecutor serviceCheckScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);

    static {
        serviceCheckScheduledThreadPoolExecutor.scheduleWithFixedDelay(ServiceManage::check, 1, 5, TimeUnit.SECONDS);
    }

    private static void check() {
        try {
            List<ServiceInfo> needRemoveList = new ArrayList<>();
            for (String serviceName : serviceInfoMap.keySet()) {
                //防御性拷贝
                List<ServiceInfo> sList = new ArrayList<>(serviceInfoMap.getOrDefault(serviceName, new ArrayList<>()));
                for (ServiceInfo serviceInfo : sList) {
                    URL url = null;
                    HttpURLConnection co = null;
                    try {
                        url = new URL(String.format("%s://%s:%s", serviceInfo.getSchema(), serviceInfo.getIp(), serviceInfo.getPort()));
                        co = (HttpURLConnection) url.openConnection();
                        co.setConnectTimeout(2000);
                        co.connect();
                    } catch (Exception ex) {
                        log.info("need autoRemove error serviceInfo: {}", serviceInfo);
                        log.error(ex.getMessage(), ex);
                        needRemoveList.add(serviceInfo);
                    } finally {
                        if (co != null) {
                            co.disconnect();
                            co = null;
                        }
                        if (url != null) {
                            url = null;
                        }
                    }
                }
            }
            if (!needRemoveList.isEmpty()) {
                for (ServiceInfo serviceInfo : needRemoveList) {
                    remove(serviceInfo.getName(), serviceInfo.getIp(), serviceInfo.getPort());
                }
            }
        } catch (Throwable ex) {
            log.error(ex.getMessage(), ex);
        }
    }

    public static Boolean exist(String serviceName, String ip, int port) {
        return ServiceNameReenTranLock.lockAndUnLock(serviceName, () -> {
            List<ServiceInfo> serviceList = serviceInfoMap.getOrDefault(serviceName, new ArrayList<>());
            ServiceInfo first = serviceList.stream().filter(s -> Objects.equals(s.getIp(), ip) && Objects.equals(s.getPort(), port)).findFirst().orElse(null);
            return first != null;
        }, reenTranLockTimeOutSeconds);
    }

    public static void add(ServiceInfo serviceInfo) {
        ServiceNameReenTranLock.lockAndUnLock(serviceInfo.getName(), () -> {
            List<ServiceInfo> serviceList = serviceInfoMap.getOrDefault(serviceInfo.getName(), new ArrayList<>());
            ServiceInfo first = serviceList.stream().filter(s -> Objects.equals(s.getIp(), serviceInfo.getIp()) && Objects.equals(s.getPort(), serviceInfo.getPort())).findFirst().orElse(null);
            if (first == null) {
                serviceList.add(serviceInfo);
                serviceInfoMap.put(serviceInfo.getName(), serviceList);
            }
            return true;
        }, reenTranLockTimeOutSeconds);
    }

    public static void remove(String serviceName, String ip, int port) {
        ServiceNameReenTranLock.lockAndUnLock(serviceName, () -> {
            List<ServiceInfo> serviceList = serviceInfoMap.getOrDefault(serviceName, new ArrayList<>());
            serviceList.removeIf(s -> Objects.equals(s.getIp(), ip) && Objects.equals(s.getPort(), port));
            serviceInfoMap.put(serviceName, serviceList);
            return true;
        }, reenTranLockTimeOutSeconds);
    }

    public static void changeCanUse(String serviceName, String ip, int port, boolean canUse) {
        ServiceNameReenTranLock.lockAndUnLock(serviceName, () -> {
            List<ServiceInfo> serviceList = serviceInfoMap.getOrDefault(serviceName, new ArrayList<>());
            ServiceInfo first = serviceList.stream().filter(s -> Objects.equals(s.getIp(), ip) && Objects.equals(s.getPort(), port)).findFirst().orElse(null);
            if (first != null) {
                first.setCanUse(canUse);
            }
            return true;
        }, reenTranLockTimeOutSeconds);
    }

    public static List<ServiceInfo> findCanUse(String serviceName) {
        return ServiceNameReenTranLock.lockAndUnLock(serviceName, () -> {
            List<ServiceInfo> returnList = new ArrayList<>();
            List<String> serviceNameList = serviceInfoMap.keySet().stream().filter(sn -> Objects.equals(sn, serviceName)).collect(Collectors.toList());
            if (!serviceNameList.isEmpty()) {
                for (String sname : serviceNameList) {
                    List<ServiceInfo> serviceList = serviceInfoMap.getOrDefault(sname, new ArrayList<>());
                    List<ServiceInfo> canUseList = serviceList.stream().filter(s -> Objects.equals(s.getCanUse(), true)).collect(Collectors.toList());
                    if (!canUseList.isEmpty()) {
                        returnList.addAll(canUseList);
                    }
                }
                return returnList;
            } else {
                return new ArrayList<>();
            }
        }, reenTranLockTimeOutSeconds);
    }

    public static List<ServiceInfo> findAll(String serviceName) {
        return ServiceNameReenTranLock.lockAndUnLock(serviceName, () -> {
            List<ServiceInfo> returnList = new ArrayList<>();
            List<String> serviceNameList = serviceInfoMap.keySet().stream().filter(sn -> Objects.equals(sn, serviceName) || serviceName == null).collect(Collectors.toList());
            if (!serviceNameList.isEmpty()) {
                for (String sname : serviceNameList) {
                    List<ServiceInfo> serviceList = serviceInfoMap.getOrDefault(sname, new ArrayList<>());
                    if (!serviceList.isEmpty()) {
                        returnList.addAll(serviceList);
                    }
                }
                return returnList;
            } else {
                return new ArrayList<>();
            }
        }, reenTranLockTimeOutSeconds);
    }

}
